package com.easylinkin.linkappapi.device.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.easylinkin.bases.redis.util.RedisUtil;
import com.easylinkin.linkappapi.common.service.CommonService;
import com.easylinkin.linkappapi.common.utils.DateUtil;
import com.easylinkin.linkappapi.common.utils.excel.ExcelConstant;
import com.easylinkin.linkappapi.common.utils.excel.ExcelTools;
import com.easylinkin.linkappapi.common.utils.io.OutputStreamUtil;
import com.easylinkin.linkappapi.device.constant.DeviceConstant.ModelCategoryEnum;
import com.easylinkin.linkappapi.device.dao.SprayRecordsMapper;
import com.easylinkin.linkappapi.device.entity.Device;
import com.easylinkin.linkappapi.device.entity.SprayRecords;
import com.easylinkin.linkappapi.device.entity.vo.SprayRecordsVo;
import com.easylinkin.linkappapi.device.entity.vo.SprayStatisticsVo;
import com.easylinkin.linkappapi.device.service.DeviceService;
import com.easylinkin.linkappapi.device.service.SprayRecordsService;
import com.easylinkin.linkappapi.device.util.DeviceUtil;
import com.easylinkin.linkappapi.deviceattributestatus.entity.DeviceAttributeStatus;
import com.easylinkin.linkappapi.deviceattributestatus.service.DeviceAttributeStatusService;
import com.easylinkin.linkappapi.openapi.dto.DatapushDTO;
import com.easylinkin.linkappapi.ruleengine.entity.RuleEngine;
import com.easylinkin.linkappapi.ruleengine.entity.RuleExecution;
import com.easylinkin.linkappapi.ruleengine.service.RuleEngineService;
import com.easylinkin.linkappapi.security.context.LinkappUserContextProducer;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

/**
 * SprayRecords表服务实现类
 *
 * @author CodeGenerator
 * @date 2022/06/27
 */
@Slf4j
@Service("appSprayRecordsService")
public class SprayRecordsServiceImpl extends ServiceImpl
        <SprayRecordsMapper, SprayRecords> implements SprayRecordsService {
    @Resource
    private CommonService commonService;
    @Resource
    private LinkappUserContextProducer linkappUserContextProducer;
    @Resource
    private DeviceService deviceService;
    @Resource
    private RedisUtil redisUtil;
    @Resource
    private DeviceAttributeStatusService deviceAttributeStatusService;
    @Resource
    @Lazy
    private RuleEngineService ruleEngineService;

    @Override
    public boolean saveOne(SprayRecords appSprayRecords) {
        commonService.setCreateAndModifyInfo(appSprayRecords);
        appSprayRecords.setTenantId(linkappUserContextProducer.getNotNullCurrent().getTenantId());
        return save(appSprayRecords);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateOne(SprayRecords appSprayRecords) {
        Assert.notNull(appSprayRecords.getId(), "id不能为空");
        commonService.setModifyInfo(appSprayRecords);
        appSprayRecords.setTenantId(linkappUserContextProducer.getNotNullCurrent().getTenantId());
        return updateById(appSprayRecords);
    }

    @Override
    public IPage<SprayRecords> selectPage(Page page, SprayRecordsVo appSprayRecords) {
        appSprayRecords.setTenantId(linkappUserContextProducer.getNotNullCurrent().getTenantId());
        return baseMapper.findPage(page, appSprayRecords);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteByIds(List<Long> idList) {
        return removeByIds(idList);
    }

    @Override
    public void export(SprayRecordsVo appSprayRecords, HttpServletRequest request, HttpServletResponse
            response) {

        IPage<SprayRecords> page = selectPage(new Page(0, -1), appSprayRecords);
        List<SprayRecords> records = page.getRecords();
//        List<SprayRecordsVo> appSprayRecordsVos = new ArrayList<>();
//        for (SprayRecords expert : records) {
//            appSprayRecordsVos.add(new SprayRecordsVo(expert));
//        }

        String keyValue = "设备编号:deviceCode,触发源:triggerSource,喷淋时长:sprayHours,喷淋开始时间:startTime,喷淋结束时间:endTime";
        String title = "SprayRecords导出数据";
        String fileName = title + ".xls";
        try {
            OutputStream outputStream = OutputStreamUtil
                    .getOutputStream(request, response, fileName);
            ExcelTools.exportExcel(outputStream, keyValue, records, ExcelConstant.XLS, title);
            response.flushBuffer();
            outputStream.close();
        } catch (IOException e) {
            log.error("excel导出失败", e);
            throw new RuntimeException("excel导出失败！IOException异常" + e.getMessage());
        } catch (Exception e) {
            log.error("excel导出失败", e);
            throw new RuntimeException("excel导出失败！" + e.getMessage());
        }
    }

    @Override
    public SprayRecords getOneById(Serializable id) {
        return baseMapper.getOneById(id);
    }

    @Override
    public void datapushHandler(DatapushDTO datapushDTO) {
        // 流水处理流程
        JSONObject data = datapushDTO.getData();
        String deviceCode = datapushDTO.getDevice_id();
//            Device device = new Device();
//            device.setCode(deviceCode);
//            List<Device> deviceList = deviceService.selectDevices(device);
//            if (deviceList == null || deviceList.size() <= 0) {
//                return ;
//            }
//            device = deviceList.get(0);
        //将查询逻辑放入主线程
        Device device = datapushDTO.getDevice_data_latest();
        if(!DeviceUtil.isBelongSpecifiedDeviceType(device,ModelCategoryEnum.SPARY.getDeviceTypeNameKey())){
            return;
        }
        if(null == data.getInteger("switch_state")){
            return;
        }
        Integer switchState = data.getInteger("switch_state");
        // 流水为下行服务
        String info = datapushDTO.getInfo();
        if(null == switchState){
            return;
        }
        List<SprayRecords> list = baseMapper.findNoEndTimeByDeviceCode(deviceCode);
        // 触发开启
        // 0 - 分闸
        // 1 - 合闸
        if(Integer.valueOf(1).equals(switchState)){
            // 手动触发 新增喷淋开启记录
            if(CollectionUtil.isEmpty(list)&&StringUtils.isEmpty(info)){
                SprayRecords sprayRecords = new SprayRecords();
                sprayRecords.setTenantId(device.getTenantId());
                sprayRecords.setDeviceCode(deviceCode);
                sprayRecords.setTriggerSource("手动触发");
                sprayRecords.setTriggerTime(new Date());
                sprayRecords.setStartTime(new Date());
                sprayRecords.setCreateTime(new Date());
                baseMapper.insert(sprayRecords);
            }
            // 规则引擎触发下行服务，设备流水 更新喷淋记录 开始时间
            if(CollectionUtil.isNotEmpty(list)&&list.size() >= 1&&!StringUtils.isEmpty(info)){
                Date startTime = new Date();
                SprayRecords sprayRecords = list.get(0);
                sprayRecords.setStartTime(startTime);
                sprayRecords.setModifyTime(new Date());
                baseMapper.updateById(sprayRecords);
            }
        }else{
            // 合闸 更新喷淋记录 结束时间和喷淋时长
            if(CollectionUtil.isNotEmpty(list)&&list.size() >= 1){
                Date endTime = new Date();
                SprayRecords sprayRecords = list.get(0);
                sprayRecords.setEndTime(endTime);
                sprayRecords.setSprayHours(calculateHours(sprayRecords.getStartTime(),endTime));
                sprayRecords.setModifyTime(new Date());
                baseMapper.updateById(sprayRecords);
            }
        }
    }

    @Override
    public SprayStatisticsVo getSprayStatistics(SprayRecordsVo sprayRecords) {
        SprayStatisticsVo sprayStatisticsVo = new SprayStatisticsVo();
        sprayStatisticsVo.setTodaySprayHours(0d);
        sprayStatisticsVo.setDayAvgSprayHours(0d);
        sprayStatisticsVo.setTodaySprayHours(0d);
        sprayStatisticsVo.setSprayStatus(1);//默认关闭
        // 获取喷淋统计数据
        sprayRecords.setTenantId(linkappUserContextProducer.getNotNullCurrent().getTenantId());
        // 累计喷淋时长
        Double totalSprayHours = baseMapper.getSprayHoursByTime(sprayRecords);
        if(null != totalSprayHours){
            sprayStatisticsVo.setTotalSprayHours(totalSprayHours);
        }
        sprayRecords.setStartTime(DateUtil.getCurrentDayMixDate());
        sprayRecords.setEndTime(DateUtil.getCurrentDayMaxDate());
        // 今日喷淋时长
        Double todaySprayHours = baseMapper.getSprayHoursByTime(sprayRecords);
        if(null != todaySprayHours){
            if(todaySprayHours > Double.valueOf(24d)){
                sprayStatisticsVo.setTodaySprayHours(Double.valueOf(24d));
            }else{
                sprayStatisticsVo.setTodaySprayHours(todaySprayHours);
            }
        }
        // 日均喷淋时长
        Double avgDaySprayHours = baseMapper.getAvgDaySprayHours(sprayRecords);
        if(null != avgDaySprayHours){
            sprayStatisticsVo.setDayAvgSprayHours(avgDaySprayHours);
            if(avgDaySprayHours > Double.valueOf(24d)){
                sprayStatisticsVo.setDayAvgSprayHours(Double.valueOf(24d));
            }else{
                sprayStatisticsVo.setDayAvgSprayHours(avgDaySprayHours);
            }
        }
        //获取最近的一条记录的时间作为展示时间
        SprayRecords sprayRecord = baseMapper.getLatestRecord(sprayRecords);
        if(sprayRecord!=null){
            sprayStatisticsVo.setEndTime(sprayRecord.getEndTime());
        }
        // 查询喷淋设备喷淋状态
        Device device = new Device();
        device.setCode(sprayRecords.getDeviceCode());
        List<Device> deviceList = deviceService.selectDevices(device);
        if (!CollectionUtil.isEmpty(deviceList)) {
            device = deviceList.get(0);
            DeviceAttributeStatus req = new DeviceAttributeStatus();
            req.setPropCode("switch_state");
            req.setDeviceCode(sprayRecords.getDeviceCode());
            req.setVersion(device.getDeviceUnit().getVersion());
            List<DeviceAttributeStatus> deviceRealtimeData = deviceAttributeStatusService
                .getDeviceRealtimeData(req);
            if(!CollectionUtil.isEmpty(deviceRealtimeData)){
                DeviceAttributeStatus switchStateStatus = deviceRealtimeData.get(0);
                if(!StringUtils.isEmpty(switchStateStatus.getPropValue())){
                    Integer sprayStatus = Integer.parseInt(switchStateStatus.getPropValue());
                    sprayStatisticsVo.setSprayStatus(sprayStatus);
                }
            }
        }

        return sprayStatisticsVo;
    }

    private Double calculateHours(Date start,Date end){
        long startTime = start.getTime();
        long endTime = end.getTime();
        long times = endTime - startTime;
        double hours = (double) times/(60*60*1000);
        BigDecimal a = BigDecimal.valueOf(hours);
        double waitTime = a.setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue();
        return waitTime;
    }

    @Override
    public void downSendTriggerSpray(Map<String, Object> downParameter, RuleExecution ruleExecution,
        String deviceCode) {
        String ruleEngineId = ruleExecution.getRuleEngineId();
        if(StringUtils.isEmpty(ruleEngineId)){
            return;
        }
        RuleEngine ruleEngine = ruleEngineService.getById(ruleEngineId);
        if(null == ruleEngine){
            return;
        }
        String triggerSource = ruleEngine.getName();
        downSendSpray(downParameter,triggerSource,ruleEngineId,deviceCode);
    }

    @Override
    public void downSendSpray(Map<String, Object> downParameter) {
        String deviceCode = (String) downParameter.get("device_id");
        downSendSpray(downParameter,"远程手动触发",null,deviceCode);
    }

    private void downSendSpray(Map<String, Object> downParameter, String triggerSource,String remark,
        String deviceCode) {
        // 校验是否是喷淋设备
        Device device = new Device();
        device.setCode(deviceCode);
        List<Device> deviceList = deviceService.selectDevices(device);
        if (deviceList == null || deviceList.size() <= 0) {
            return ;
        }
        device = deviceList.get(0);
        if(!DeviceUtil.isBelongSpecifiedDeviceType(device,ModelCategoryEnum.SPARY.getDeviceTypeNameKey())){
            return;
        }
        Map<String,Object> map = (Map<String, Object>) downParameter.get("parameter");
        if(null == map.get("switch_state")){
            return;
        }
        Integer switchState = Integer.parseInt(String.valueOf(map.get("switch_state")));
        if(null == switchState){
            return;
        }
        List<SprayRecords> list = baseMapper.findNoEndTimeByDeviceCode(deviceCode);
        if(Integer.valueOf(1).equals(switchState)&&CollectionUtil.isEmpty(list)){
            SprayRecords sprayRecords = new SprayRecords();
            sprayRecords.setTenantId(device.getTenantId());
            sprayRecords.setDeviceCode(deviceCode);
            sprayRecords.setTriggerSource(triggerSource);
            sprayRecords.setTriggerTime(new Date());
            sprayRecords.setStartTime(new Date());
            sprayRecords.setCreateTime(new Date());
            sprayRecords.setRemark(remark);
            baseMapper.insert(sprayRecords);
        }
    }

    @Override
    public List<SprayRecords> getTrend(SprayRecords appSprayRecords) {
        appSprayRecords.setTenantId(linkappUserContextProducer.getNotNullCurrent().getTenantId());
        List<SprayRecords> trend = baseMapper.getTrend(appSprayRecords);
        if(CollectionUtil.isNotEmpty(trend)){
            trend.stream().forEach(p->{
                if(p.getSprayHours() > Double.valueOf(24d)){
                    p.setSprayHours(Double.valueOf(24d));
                }else{
                    p.setSprayHours(p.getSprayHours());
                }
            });
        }
        return trend;
    }
}

